package cn.itsource.hrm.config;

import cn.itsource.hrm.domain.Permission;
import cn.itsource.hrm.handler.MyAccessDeniedHandler;
import cn.itsource.hrm.handler.MyAuthenticationEntryPoint;
import cn.itsource.hrm.handler.MyAuthenticationFailureHandler;
import cn.itsource.hrm.handler.MyAuthenticationSuccessHandler;
import cn.itsource.hrm.mapper.PermissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;
import java.util.List;

//通过继承这个类来完成security自定义配置
//1 加载认证用户信息
//2 配置密码加密器
//3 配置认证授权规则，哪些要放行那要认证
@Configuration //交给spring管理
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled= true) //开启注解权限
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private PermissionMapper permissionMapper;
    //1 提供一个UserDetailsService，就可以加载认证用户信息UserDetails
    /*@Bean
    public UserDetailsService userDetailsService(){
        //Manager就相当于service
        InMemoryUserDetailsManager detailsManager = new InMemoryUserDetailsManager();

        //User.withUsername("zs").password("1")数据库用户
        //.authorities("admin").数据权限
        UserDetails userDetails = User.withUsername("zs").password("1").authorities("admin").build();
        detailsManager.createUser(userDetails);
        return detailsManager;
    }*/
    //2 配置密码加密器
    @Bean
    public PasswordEncoder passwordEncoder(){

        //私有构造不能new，静态方法获取
//        return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    //3 配置认证授权规则，哪些要放行那要认证
    @Override //重写http认证
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.authorizeRequests() //授权配置
                .antMatchers("/login").permitAll()//登录放行
                .antMatchers("/login.html").permitAll() //登录页面放行
                /* web授权方案1：写死
                .antMatchers("/user/list").hasRole("admin") ///user/list资源需要admin角色才能
                .antMatchers("/user/add").hasAnyRole("admin","hrm") // /user/list资源任意一个角色都可以
                .antMatchers("/user/edit").hasAuthority("user:edit") ///user/edit资源必须是user：edit的权限才能访问
                .antMatchers("/user/get").hasAnyAuthority("user:edit","user:*") ///user/edit资源任意一个权限就OK
                */
                .anyRequest().authenticated()//其他请求登录登录
                .and().formLogin()//表单登录
                .loginPage("/login.html") //指定登录页面
                .loginProcessingUrl("/login") //登录处理地址
                 //.successForwardUrl("/loginSuccess")//登录成功后跳转页面地址，前后端分离应该json数据
                .successHandler(new MyAuthenticationSuccessHandler())
                .failureHandler(new MyAuthenticationFailureHandler())
                .and().exceptionHandling()
                .accessDeniedHandler(new MyAccessDeniedHandler()) //没有权限访问
                .authenticationEntryPoint(new MyAuthenticationEntryPoint()) //匿名处理
                .and().logout().permitAll() //登出放行
                .and().csrf().disable();//关闭跨站请求伪造


        //记住我-persistentTokenRepository()和注入一样
        httpSecurity.rememberMe().tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(3600)
                .userDetailsService(userDetailsService);
    }

    //web授权方案2：动态添加授权:从数据库动态查询出，哪些资源需要什么样的权限
    /*
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        List<Permission> permissions = permissionMapper.loadAll();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                expressionInterceptUrlRegistry = http.csrf().disable()   //关闭CSRF跨站点请求伪造防护
                .authorizeRequests()          //对请求做授权处理
                .antMatchers("/login").permitAll()  //登录路径放行
                .antMatchers("/login.html").permitAll();//对登录页面跳转路径放行

        //web授权方案2：动态添加授权:从数据库动态查询出，哪些资源需要什么样的权限
        for(Permission permission : permissions){
            System.out.println(permission.getResource()+" - "+permission.getSn());
            //如： /employee/list    需要     employee:list 权限才能访问
            expressionInterceptUrlRegistry.antMatchers(permission.getResource()).hasAuthority(permission.getSn());
        }

        expressionInterceptUrlRegistry
                .anyRequest().authenticated() //其他路径都要拦截
                .and().formLogin()  //允许表单登录， 设置登陆页
                .successForwardUrl("/loginSuccess") // 设置登陆成功页
                .loginPage("/login.html")   //登录页面跳转地址
                .loginProcessingUrl("/login")   //登录处理地址
                .and().logout().permitAll();    //登出

    }*/


    //==========记住我========//
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
        repository.setDataSource(dataSource); //要访问数据库就要设置数据源
        //每次启动创建表
        //repository.setCreateTableOnStartup(true);
        return repository;
    }

    public static void main(String[] args) {
        BCryptPasswordEncoder  encoder = new BCryptPasswordEncoder();
        System.out.println(encoder.encode("1"));
        System.out.println(encoder.encode("1"));
    }
}
